/*
 *  serveur.c
 *  pire2pireV1
 *
 *  Created by jihed kamkoum on 04/05/10.
 *  Copyright 2010 ENSEIRB. All rights reserved.
 *
 */

#include "serveur.h"
void setnonblocking(int sock){
	int opts;
	opts = fcntl(sock,F_GETFL);
	if (opts < 0) {
		perror("fcntl(F_GETFL)");
		exit(EXIT_FAILURE);
	}
	opts = (opts | O_NONBLOCK);
	if (fcntl(sock,F_SETFL,opts) < 0) {
		perror("fcntl(F_SETFL)");
		exit(EXIT_FAILURE);
	}
}
void build_select_list(int sock,fd_set* socks,int * connectlist,int* highsock,int fdpipe ) {
	int listnum;	   
	FD_ZERO(socks);
	FD_SET(sock,socks);
	FD_SET(fdpipe,socks);
	if (fdpipe > *highsock)
		*highsock = fdpipe;
	for (listnum = 0; listnum < FDMAX; listnum++) {
		if (connectlist[listnum] != 0) {
			FD_SET(connectlist[listnum],socks);
			if (connectlist[listnum] > *highsock)
				*highsock = connectlist[listnum];
		}
	}
}


void handle_new_connection(int sock,int *connectlist,clientlist_t * listeClients,pthread_mutex_t *clientTableAccess) {
	int listnum;
	int connection;
	SOCKADDR_IN csin;
	socklen_t crecsize = sizeof(csin);
	//timeout_handler(connectlist,listeClients,30000.0);// 300 : temps max sans activitee
	connection = accept(sock, (SOCKADDR*)&csin, &crecsize);
	if (connection < 0) {
		perror("accept");
		exit(EXIT_FAILURE);
	}
	setnonblocking(connection);
	for (listnum = 0; (listnum < FDMAX) && (connection != -1); listnum ++)
		if (connectlist[listnum] == 0) {
			printf("\nConnection accepted:   FD=%d; Slot=%d\n",connection,listnum);
			connectlist[listnum] = connection;
			client_t *client;
			client = malloc(sizeof(client_t));
			//strcpy(client->ip,(const char *)inet_ntoa(csin.sin_addr));
			client->port_c = htons(csin.sin_port);
			client->last_event = clock();
			client->isclient = VRAI;
			client->socket = connection;
			sleep(1);
			char buffer[BUFSIZ] = "";
			if((recv(connection, buffer, sizeof(buffer), 0))==-1)
				perror("receive");
			else
				printf("Chaine recus : %s\n", buffer);
			//debut section critique
			char ip[TAILLE_IP];
			int port;
			sscanf(buffer,"%s : %d",ip,&port);
			strcpy(client->ip,(const char *)ip);
			client->port_s = port;
			pthread_mutex_lock(clientTableAccess);
			clientlist_insert(listeClients, client);
			pthread_mutex_unlock(clientTableAccess);
			//fin section critique
			connection = -1;
		}
	if (connection != -1) {
		/* No room left in the queue! */
		printf("\nNo room left for new client.\n");
		//envoyer un message au client
		sock_puts(connection, "Sorry, this server is too busy. Try again later!\r\n");
		close(connection);
	}
	
}

void bind_serveur(binder * b){
	sleep(rand()%3);
	loop1 :
	*b->listeFichiers = malloc(sizeof(filelist_t));
	calculemd5(b->semid,*b->path, *b->listeFichiers, b->fileTableAccess,*b->ip,*b->port); 

	if(*b->sock!=-1){
		printf("Fermeture de la socket serveur\n");
		if((close(*b->sock)==-1))
			perror("close");
		printf("Fermeture du serveur terminée\n");
		//liberation des tables
		pthread_mutex_lock(b->clientTableAccess);
		clientlist_delete(*b->listeClients);
		pthread_mutex_unlock(b->clientTableAccess);
	}	
	create_serveur_sock(b->sock);
	//upload Table
	pthread_mutex_lock(b->uploadTableAccess);
	*b->listeUpload=malloc(sizeof(ListeUpload));
	init_listeUpload(*b->listeUpload);
	pthread_mutex_unlock(b->uploadTableAccess);
	//
	//
	//
	int reuse_addr = 1;
	struct timeval timeout; 
	int readsocks;
	char events[80];
	//memset((char *) &events, "\0", sizeof(events));
	setsockopt(*b->sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr));
	setnonblocking(*b->sock);
	
    SOCKADDR_IN sin;
	bzero((char*)&sin, sizeof(SOCKADDR_IN));
    socklen_t recsize = sizeof(sin);	
    int sock_err;
	//debut section critique
	pthread_mutex_lock(b->clientTableAccess);
	//free(*b->listeClients);
	*b->listeClients =clientlist_new();
	pthread_mutex_unlock(b->clientTableAccess);
	
	//fin section critique
	//debut section critique
	// fin section critique
	
	/* Configuration */
	sin.sin_addr.s_addr = inet_addr(*b->ip);
	printf("%s\n",inet_ntoa(sin.sin_addr));
	sin.sin_family = AF_INET;                 /* Protocole familial (IP) */
	sin.sin_port = htons(*b->port);/* Listage du port */
	//ajouter le serveur dans la liste des voisin
	client_t *client;
	client = malloc(sizeof(client_t));
	client->last_event = clock();
	client->socket = *b->sock;
	client->port_c = 0;
	client->isclient = FAUX;
	strcpy(client->ip,*b->ip);
	client->port_s = *b->port;
	pthread_mutex_lock(b->clientTableAccess);
	clientlist_insert(*b->listeClients, client);
	pthread_mutex_unlock(b->clientTableAccess);

	//bind the socket
	if((sock_err = bind(*b->sock, (SOCKADDR*)&sin, recsize))== SOCKET_ERROR){
		perror("Bind");
		close(*b->sock);
		exit(EXIT_FAILURE);
	}
	/* Démarrage du listage (mode server) */
	if((sock_err = listen(*b->sock, FDMAX))== SOCKET_ERROR){
		perror("listen");
	}
	printf("Listage du port %d...\n", *b->port);
	int highsock = *b->sock;
	fd_set socks;
	int connectlist[FDMAX];
	// Initialisation du tableau de socket
	memset((char *) &connectlist, 0, sizeof(connectlist));
	//int i;
	printf("Patientez pendant que le client se connecte sur le port %d...\n", *b->port);
	
	while(1)
	{
		/* Attente pendant laquelle le client se connecte */
		setnonblocking(b->fdpipe0);
		build_select_list(*b->sock,&socks,connectlist,&highsock,b->fdpipe0);
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		readsocks = select(highsock+1, &socks, (fd_set *) 0,(fd_set *) 0, &timeout);
		if (readsocks < 0) {
			perror("select");
			exit(EXIT_FAILURE);
		}
		if (readsocks == 0) {
			//printf(".");
			fflush(stdout);
			if((sched_yield()==-1)){
				perror("yield");
			}
			
		} else{
			if (FD_ISSET(b->fdpipe0,&socks)) {
				
				if((read(b->fdpipe0, events, sizeof(events))==-1)){
					perror("read");
					exit(EXIT_FAILURE);
				}
				char message[NAMESIZE];
				sscanf(events, "%s", message);
				if (strcmp(message, "set") == 0) {
					char* cmd=malloc(10*sizeof(char));
					char* param=malloc(40*sizeof(char));
					if(!decomposer_buff(events,cmd,param))//S'il n'y a pas de parametre
						param=NULL;
					
					int setret = cmd_set(param,b->ip,b->port,b->path);
					if(param!=NULL && setret==0){
						goto loop1;
					}
					else  if (setret == 1){
						calculemd5(b->semid,*b->path, *b->listeFichiers, b->fileTableAccess,*b->ip,*b->port);
					}

					free(cmd);
					free(param);
				}
				else if(strcmp(message, "upload") == 0){
					pthread_mutex_lock(b->uploadTableAccess);
					afficher_listeUpload2(*b->listeUpload,*b->ip,*b->port);
					pthread_mutex_unlock(b->uploadTableAccess);
				}
			}
			read_socks(&socks,connectlist,b);
		}
		
		
	}
	
}

void read_socks(fd_set *socks,int * connectlist,binder * b) {
	int listnum;
	if (FD_ISSET(*b->sock,socks))
		handle_new_connection(*b->sock,connectlist,*b->listeClients,b->clientTableAccess);
	for (listnum = 0; listnum < FDMAX; listnum++) {
		if (FD_ISSET(connectlist[listnum],socks)){
			//debut section critique
			pthread_mutex_lock(b->clientTableAccess);
			if((findBySocket(connectlist[listnum], *b->listeClients))!=NULL){
				(findBySocket(connectlist[listnum], *b->listeClients))->last_event = clock();
			}
			pthread_mutex_unlock(b->clientTableAccess);
			//fin section critique
			deal_with_data(listnum,connectlist,b);
			
		}
	}
}
void deal_with_data(int listnum,int * connectlist,binder *b){
	
	char buffer[BUFSIZ];
	memset((char *) &buffer, 0, sizeof(buffer));
	int ret;
	struct sembuf op;
	op.sem_num = 0; //Test si le thread est dispo
	op.sem_op = -1; //P()
	op.sem_flg = 0; //attente de l'operation
	//pthread_mutex_lock(&b->clientSocketAccess[listnum]);
	if((ret=recv(connectlist[listnum], buffer, sizeof(buffer), 0))==-1){
		//pthread_mutex_unlock(&b->clientSocketAccess[listnum]);
		perror("receive");
	}
	else if(ret !=0){
		//pthread_mutex_unlock(&b->clientSocketAccess[listnum]);
		if((semop(b->semid, &op, 1)==0)){
			/*thread disponnible*/
			pthread_t threadClient;
			/*creation du thread*/
			//les arguments de la thread
			cmdrecv * recived = malloc(sizeof(cmdrecv));
			recived->sock = connectlist[listnum];
			recived->semid = b->semid;
			recived->cmd = buffer;
			recived->listeClients = *b->listeClients;
			recived->clientTableAccess = b->clientTableAccess;
			recived->listeFichiers = *b->listeFichiers;
			recived->fileTableAccess = b->fileTableAccess;
			recived->ip_s = *b->ip;
			recived->port_s = *b->port;
			recived->uploadTableAccess = b->uploadTableAccess;
			recived->listeUpload = *b->listeUpload;
			recived->path = *b->path;
			recived->listeDownload = *b->listeDownload;
			recived->downloadTableAccess = b->downloadTableAccess;
			recived->clientSocketAccess = &b->clientSocketAccess[listnum];
			recived->pipe1 = b->fdpipes1;
			recived->neighbourhoodCond = b->neighbourhoodCond;
			recived->neighbourhoodCondMutex = b->neighbourhoodCondMutex;
			recived->neighbourhoodbool = b->neighbourhoodbool;
			recived->listCond = b->listCond;
			recived->listCondMutex = b->listCondMutex;
			recived->listbool = b->listbool;
			recived->csock = *b->csock;
			recived->trafficCondMutex = b->trafficCondMutex;
			recived->trafficbool = b->trafficbool;
			recived->trafficCond = b->trafficCond;
			//recived->
			printf("Chaine recue : %s\n",buffer);
			if((pthread_create(&threadClient, NULL,(void *)f_thread_serveur,recived))==-1)
				printf("probleme pthread_create \n");
			else {
				changer_ordo_priorite(threadClient, SCHED_RR, 5);
			}
		}
		else{
			perror("semop P()");
		}		
	}
	
}
//Routine de traitement de la requete client
void * f_thread_serveur(cmdrecv * recived){
	printf("debut thread %d\n",pthread_self());
	sleep(2);
	int semid = recived->semid;
	char message[NAMESIZE];
	sscanf(recived->cmd, "%s", message);	
	/*debut traitement*/
	if (strcmp(message, "get") == 0) {
		cmd_get_handler(recived);
	}
	else if (strcmp(message, "neighbourhood") == 0) {

		if (recived->csock!=-1) {
			if((write(recived->pipe1,"neighbourhood" , (14))==-1)){
				perror("write");
				exit(EXIT_FAILURE);
			}
			//ajouter un timer
			pthread_mutex_lock(recived->neighbourhoodCondMutex);

			//blockage sur une condition
			while ((*recived->neighbourhoodbool) == FAUX) {
				pthread_cond_wait(recived->neighbourhoodCond, recived->neighbourhoodCondMutex);
			}
			(*recived->neighbourhoodbool) = FAUX;
			pthread_mutex_unlock(recived->neighbourhoodCondMutex);
		}
		sched_yield();
		cmd_neighbourhoud_handler(recived);
		
	}
	else if (strcmp(message, "list") == 0) {

		if (recived->csock!=-1) {
			if((write(recived->pipe1,"list" , (14))==-1)){
				perror("write");
				exit(EXIT_FAILURE);
			}
			time_t debut=time(NULL),duree=0;
			pthread_mutex_lock(recived->listCondMutex);
		       
			//blockage sur une condition
			while (((*recived->listbool) == FAUX)&&(duree<2)) {
				pthread_cond_wait(recived->listCond, recived->listCondMutex);
				duree=time(NULL)-debut;
			}
			(*recived->listbool) = FAUX;
			pthread_mutex_unlock(recived->listCondMutex);
		}
		sched_yield();
		cmd_list_handler(recived);
		
	}
	else if (strcmp(message, "traffic") == 0) {

		if (recived->csock!=-1) {
			if((write(recived->pipe1,"traffic" , (14))==-1)){
				perror("write");
				exit(EXIT_FAILURE);
			}
			//ajouter un timer
			pthread_mutex_lock(recived->trafficCondMutex);
			
			//blockage sur une condition
			while ((*recived->trafficbool) == FAUX) {
				pthread_cond_wait(recived->trafficCond, recived->trafficCondMutex);
			}
			(*recived->trafficbool) = FAUX;
			pthread_mutex_unlock(recived->trafficCondMutex);
		}
		sched_yield();
		cmd_traffic_handler(recived);

	}
	else if (strcmp(message, "raw") == 0) {
		cmd_raw_handler(recived);
		
	}
	else if (strcmp(message, "checksum") == 0) {
		cmd_checksum_handler(recived);

	}
	/*Liberation d'un thread client*/
	struct sembuf op;
	op.sem_num = 0; //Test si le thread est dispo
	op.sem_op = 1; //V()
	op.sem_flg = 0; //aucune importance
	if((semop(semid, &op, 1)==-1)){
		perror("semop V()");
	}
	printf("fin thread %d\n",pthread_self());
	pthread_exit(NULL);
}
